package main

import (
	"fmt"
	"sync"
	"time"
)

func OnceX[T any](initializer func() T) func() T {
	var once sync.Once
	var t T
	f := func() {
		t = initializer()
	}
	return func() T {
		once.Do(f)
		return t
	}
}

func runExtOnce() {
	count := 42
	var getMol = OnceX(func() int {
		count++
		return count
	})
	getMol()
	fmt.Println(count) //43
	getMol()
	fmt.Println(count) //43
}

// 到当前文件 目录下执行 go run .
func main() {
	runExtOnce()
}

func runOnceFunWithAPanic() {
	var o sync.Once
	o.Do(func() {
		panic("test to panic")
	})
	fmt.Println("run here") // 不会执行到这，程序结束于panic
}

func runAfterDoAlways() {
	var m map[int]int
	var o sync.Once
	initM := func() {
		time.Sleep(2 * time.Second)
		m = make(map[int]int)
	}
	go o.Do(initM)
	//time.Sleep(time.Second)
	o.Do(initM) // 当跨go程时，并不能确定另一边的 Do执行完了。再调一次Do，标准库的实现中，因为内部有锁，会阻塞在这里直到第一个Do执行完成
	//内部有双重检查，即使等待后取到锁了，也不会重复执行。
	m[1] = 1
}
